package io.kiki.sba.registry.server.metadata.lease.impl;


import io.kiki.sba.registry.common.model.Node;
import io.kiki.sba.registry.common.model.metaserver.Lease;
import io.kiki.sba.registry.exception.DisposeException;
import io.kiki.sba.registry.exception.InitializeException;
import io.kiki.sba.registry.server.metadata.lease.Evictable;
import io.kiki.sba.registry.util.ConcurrentUtils;
import io.kiki.sba.registry.util.WakeUpLoopExecuteTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;


public abstract class AbstractEvictableLeaseManager<T extends Node> extends LeaderAwareLeaseManager<T> implements Evictable {
    private static final Logger logger = LoggerFactory.getLogger(AbstractEvictableLeaseManager.class);

    private final AtomicLong lastEvictTime = new AtomicLong();

    private final EvictLoopExecuteTask evictTask = new EvictLoopExecuteTask();

    @Override
    public void doInitialize() throws InitializeException {
        super.doInitialize();
        ConcurrentUtils.createDaemonThread(getClass().getSimpleName(), evictTask).start();
    }

    @Override
    public void doDispose() throws DisposeException {
        evictTask.close();
        super.doDispose();
    }

    @Override
    public void evict() {
        if (lastEvictTime.get() + getEvictBetweenMilli() > System.currentTimeMillis()) {
            logger.warn("[evict][too quick] last evict time: {}", lastEvictTime.get());
            return;
        }
        lastEvictTime.set(System.currentTimeMillis());
        List<Lease<T>> list = getExpiredLeases();
        if (list.isEmpty()) {
            return;
        }

        for (Lease<T> lease : list) {
            Lease<T> doubleCheck = getLease(lease.getRenewal());
            if (doubleCheck.isExpired()) {
                logger.info("[evict]{},{}", doubleCheck.getRenewal().getNodeType(), doubleCheck);
                try {
                    cancel(lease);
                } catch (Throwable th) {
                    logger.error("[evict] node cancel failure", th);
                }
            }
        }
    }

    protected List<Lease<T>> getExpiredLeases() {
        List<Lease<T>> expires = new LinkedList<>();
        for (Lease<T> lease : VersionedLeaseList().getList()) {
            if (lease.isExpired()) {
                expires.add(lease);
            }
        }
        return expires;
    }

    protected abstract int getEvictBetweenMilli();

    protected abstract int getIntervalMilli();

    private final class EvictLoopExecuteTask extends WakeUpLoopExecuteTask {

        @Override
        public int getWaitingMillis() {
            return getIntervalMilli();
        }

        @Override
        public void _execute_() {
            if (amILeader() && metadataLeaderService.amIStableAsLeader()) {
                logger.info("[evict] begin");
                evict();
            }
        }
    }
}
